///**
//Copyright (c) 2007-2013 Alysson Bessani, Eduardo Alchieri, Paulo Sousa, and the authors indicated in the @author tags
//
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//
//http://www.apache.org/licenses/LICENSE-2.0
//
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
//*/
//package bftsmart.demo.bftmap;
//
//import bftsmart.consensus.app.BatchAppResultImpl;
//import bftsmart.tom.MessageContext;
//import bftsmart.tom.ReplyContextMessage;
//import bftsmart.tom.ServiceReplica;
//import bftsmart.tom.server.defaultservices.DefaultRecoverable;
//import bftsmart.tom.util.BytesUtils;
//
//import java.io.*;
//import java.util.List;
//import java.util.Map;
//import java.util.concurrent.locks.ReentrantLock;
//import java.util.logging.Level;
//import java.util.logging.Logger;
//
//
///**
// *
// * @author sweta
// *
// * This class will create a ServiceReplica and will initialize
// * it with a implementation of Executable and Recoverable interfaces.
// */
//public class BFTMapBatchServer extends DefaultRecoverable {
//
//	private int ops;
//
//    MapOfMaps tableMap = new MapOfMaps();
//    ServiceReplica replica = null;
//	private ReentrantLock stateLock = new ReentrantLock();
//
//    //The constructor passes the id of the server to the super class
//    public BFTMapBatchServer(int id) {
//    	replica = new ServiceReplica(id, this, this);
//    }
//
//    public static void main(String[] args){
//        if(args.length < 1) {
//            System.out.println("Use: java BFTMapServer <processId>");
//            System.exit(-1);
//        }
//        new BFTMapBatchServer(Integer.parseInt(args[0]));
//    }
//
//
//    @Override
//    public byte[][] appExecuteBatch(byte[][] commands, MessageContext[] msgCtxs, boolean fromConsensus) {
//    	byte[][] replies = new byte[commands.length][];
//    	stateLock.lock();
//    	ops += commands.length;
//    	if(ops % 5000 == 0)
//    		System.out.println("OPS: " + ops + ", CID: " + msgCtxs[0].getConsensusId());
//    	try {
//    		int index = 0;
//        	for(byte[] command: commands) {
//	            ByteArrayInputStream in = new ByteArrayInputStream(command);
//	            ByteArrayOutputStream out = null;
//	            byte[] reply = null;
//	            int cmd = new DataInputStream(in).readInt();
//	            switch (cmd) {
//	                //operations on the hashmap
//	                case BFTMapRequestType.PUT:
//	                    String tableName = new DataInputStream(in).readUTF();
//	                    String key = new DataInputStream(in).readUTF();
//	                    String value = new DataInputStream(in).readUTF();
////	                    byte[] valueBytes = value.getBytes();
//						byte[] valueBytes = BytesUtils.getBytes(value);
//	//                    System.out.println("Key received: " + key);
//	                    byte[] ret = tableMap.addData(tableName, key, valueBytes);
//	                    if (ret != null) {
////	                    	System.out.println("Return not null, overwrote data. valueBytes: " + valueBytes);
//	                        ret = new byte[0];
//
//	                    }
//	                    reply = valueBytes;
//	                    break;
//	                case BFTMapRequestType.REMOVE:
//	                    tableName = new DataInputStream(in).readUTF();
//	                    key = new DataInputStream(in).readUTF();
//	//                    System.out.println("Key received: " + key);
//	                    valueBytes = tableMap.removeEntry(tableName, key);
//	                    value = new String(valueBytes);
//	                    System.out.println("Value removed is : " + value);
//	                    out = new ByteArrayOutputStream();
//	                    new DataOutputStream(out).writeBytes(value);
//	                    reply = out.toByteArray();
//	                    break;
//	                case BFTMapRequestType.TAB_CREATE:
//	                    tableName = new DataInputStream(in).readUTF();
//	                    //ByteArrayInputStream in1 = new ByteArrayInputStream(command);
//	                    ObjectInputStream objIn = new ObjectInputStream(in);
//	                    Map<String, byte[]> table = null;
//	                    try {
//	                        table = (Map<String, byte[]>) objIn.readObject();
//	                    } catch (ClassNotFoundException ex) {
//	                        Logger.getLogger(BFTMapBatchServer.class.getName()).log(Level.SEVERE, null, ex);
//	                    }
//	                    Map<String, byte[]> tableCreated = tableMap.addTable(tableName, table);
//	                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
//	                    ObjectOutputStream objOut = new ObjectOutputStream(bos);
//	                    objOut.writeObject(tableCreated);
//	                    objOut.close();
//	                    in.close();
//	                    reply = bos.toByteArray();
//	                    break;
//	                case BFTMapRequestType.TAB_REMOVE:
//	                    tableName = new DataInputStream(in).readUTF();
//	                    table = tableMap.removeTable(tableName);
//	                    bos = new ByteArrayOutputStream();
//	                    objOut = new ObjectOutputStream(bos);
//	                    objOut.writeObject(table);
//	                    objOut.close();
//	                    objOut.close();
//	                    reply = bos.toByteArray();
//	                    break;
//
//
//	                case BFTMapRequestType.SIZE_TABLE:
//	                    int size1 = tableMap.getNumOfTables();
//	//                    System.out.println("Size " + size1);
//	                    out = new ByteArrayOutputStream();
//	                    new DataOutputStream(out).writeInt(size1);
//	                    reply = out.toByteArray();
//	                    break;
//	                case BFTMapRequestType.GET:
//	                    tableName = new DataInputStream(in).readUTF();
//	                    System.out.println("tablename: " + tableName);
//	                    key = new DataInputStream(in).readUTF();
//	//                    System.out.println("Key received: " + key);
//	                    valueBytes = tableMap.getEntry(tableName, key);
//	                    value = new String(valueBytes);
//	                    System.out.println("The value to be get is: " + value);
//	                    out = new ByteArrayOutputStream();
//	                    new DataOutputStream(out).writeBytes(value);
//	                    reply = out.toByteArray();
//	                    break;
//	                case BFTMapRequestType.SIZE:
//	                    String tableName2 = new DataInputStream(in).readUTF();
//	                    int size = tableMap.getSize(tableName2);
//	                    out = new ByteArrayOutputStream();
//	                    new DataOutputStream(out).writeInt(size);
//	                    reply = out.toByteArray();
//	                    break;
//		            case BFTMapRequestType.CHECK:
//		                tableName = new DataInputStream(in).readUTF();
//		                key = new DataInputStream(in).readUTF();
//	//	                System.out.println("Table Key received: " + key);
//		                valueBytes = tableMap.getEntry(tableName, key);
//		                boolean entryExists = valueBytes != null;
//		                out = new ByteArrayOutputStream();
//		                new DataOutputStream(out).writeBoolean(entryExists);
//		                reply = out.toByteArray();
//		                break;
//				    case BFTMapRequestType.TAB_CREATE_CHECK:
//				        tableName = new DataInputStream(in).readUTF();
//				        table = tableMap.getTable(tableName);
//				        boolean tableExists = (table != null);
//				        out = new ByteArrayOutputStream();
//				        new DataOutputStream(out).writeBoolean(tableExists);
//				        reply = out.toByteArray();
//				        break;
//	            }
//	            replies[index] = reply;
//        	}
//        } catch (IOException ex) {
//        	ex.printStackTrace();
//            Logger.getLogger(BFTMapBatchServer.class.getName()).log(Level.SEVERE, null, ex);
//            replies = null;
//        }
//        stateLock.unlock();
//        return replies;
//    }
//
//	@Override
//	public byte[][] appExecuteBatch(byte[][] commands, MessageContext[] msgCtxs, boolean fromConsensus, List<ReplyContextMessage> replyContextMessages) {
//		return appExecuteBatch(commands, msgCtxs, fromConsensus);
//	}
//
//	@Override
//    public byte[] appExecuteUnordered(byte[] command, MessageContext msgCtx) {
//    	try {
//	        ByteArrayInputStream in = new ByteArrayInputStream(command);
//	        ByteArrayOutputStream out = null;
//	        byte[] reply = null;
//	        int cmd = new DataInputStream(in).readInt();
//	        switch (cmd) {
//                case BFTMapRequestType.SIZE_TABLE:
//                    int size1 = tableMap.getNumOfTables();
////                    System.out.println("Size " + size1);
//                    out = new ByteArrayOutputStream();
//                    new DataOutputStream(out).writeInt(size1);
//                    reply = out.toByteArray();
//                    break;
//                case BFTMapRequestType.GET:
//                    String tableName = new DataInputStream(in).readUTF();
//                    System.out.println("tablename: " + tableName);
//                    String key = new DataInputStream(in).readUTF();
////                    System.out.println("Key received: " + key);
//                    byte[] valueBytes = tableMap.getEntry(tableName, key);
//                    String value = new String(valueBytes);
//                    System.out.println("The value to be get is: " + value);
//                    out = new ByteArrayOutputStream();
//                    new DataOutputStream(out).writeBytes(value);
//                    reply = out.toByteArray();
//                    break;
//                case BFTMapRequestType.SIZE:
//                    String tableName2 = new DataInputStream(in).readUTF();
//                    int size = tableMap.getSize(tableName2);
////                    System.out.println("Size " + size);
//                    out = new ByteArrayOutputStream();
//                    new DataOutputStream(out).writeInt(size);
//                    reply = out.toByteArray();
//                    break;
//	            case BFTMapRequestType.CHECK:
//	                tableName = new DataInputStream(in).readUTF();
//	                key = new DataInputStream(in).readUTF();
////	                System.out.println("Table Key received: " + key);
//	                valueBytes = tableMap.getEntry(tableName, key);
//	                boolean entryExists = valueBytes != null;
//	                out = new ByteArrayOutputStream();
//	                new DataOutputStream(out).writeBoolean(entryExists);
//	                reply = out.toByteArray();
//	                break;
//			    case BFTMapRequestType.TAB_CREATE_CHECK:
//			        tableName = new DataInputStream(in).readUTF();
////			        System.out.println("Table of Table Key received: " + tableName);
//			        Map<String, byte[]> table = tableMap.getTable(tableName);
//			        boolean tableExists = (table != null);
//			        System.out.println("Table exists: " + tableExists);
//			        out = new ByteArrayOutputStream();
//			        new DataOutputStream(out).writeBoolean(tableExists);
//			        reply = out.toByteArray();
//			        break;
//	        }
//	        return reply;
//	    } catch (IOException ex) {
//	    	ex.printStackTrace();
//	        Logger.getLogger(BFTMapBatchServer.class.getName()).log(Level.SEVERE, null, ex);
//	        return null;
//	    }
//    }
//
//    @Override
//    public byte[] getSnapshot() {
//        try {
//            ByteArrayOutputStream bos = new ByteArrayOutputStream();
//            ObjectOutput out = new ObjectOutputStream(bos);
//            out.writeObject(tableMap);
//            out.flush();
//            bos.flush();
//            out.close();
//            bos.close();
//            return bos.toByteArray();
//        } catch (IOException ex) {
//            Logger.getLogger(BFTMapBatchServer.class.getName()).log(Level.SEVERE, null, ex);
//            return new byte[0];
//        }
//    }
//
//	@Override
//	public BatchAppResultImpl preComputeAppHash(byte[][] commands) {
//		return null;
//	}
//
//	@Override
//	public List<byte[]> updateAppResponses(List<byte[]> asyncResponseLinkedList, byte[] commonHash, boolean isConsistent) {
//		return null;
//	}
//
//	@Override
//	public void preComputeAppCommit(String batchId) {
//
//	}
//
//	@Override
//	public void preComputeAppRollback(String batchId) {
//
//	}
//
//    @Override
//    public void installSnapshot(byte[] state) {
//        try {
//
//            // serialize to byte array and return
//            ByteArrayInputStream bis = new ByteArrayInputStream(state);
//            ObjectInput in = new ObjectInputStream(bis);
//            tableMap = (MapOfMaps) in.readObject();
//            in.close();
//            bis.close();
//
//        } catch (ClassNotFoundException ex) {
//            Logger.getLogger(BFTMapBatchServer.class.getName()).log(Level.SEVERE, null, ex);
//        } catch (IOException ex) {
//            Logger.getLogger(BFTMapBatchServer.class.getName()).log(Level.SEVERE, null, ex);
//        }
//    }
//
//}